﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Xml.Linq;

namespace FlickrCarousel
{
    public partial class Carousel : UserControl
    {
        public IEnumerable<FlickrPhoto> Photos { get; set; }
        public Int32 CurrentItem { get; set; }
        public Int32 SpotsMoved { get; set; }
        private Int32 AnimationRetainCount { get; set; }

        public Carousel()
        {
            InitializeComponent();
            GetPhotos();
        }

        private void GetPhotos() {
            string url = String.Format("http://api.flickr.com/services/rest/?method=flickr.people.getPublicPhotos&api_key={1}&user_id={0}",
                    (App.Current as App).FlickrUserName, (App.Current as App).APIKey, (App.Current as App).APISecret);
            
            WebClient flickrCall = new WebClient();
            flickrCall.DownloadStringCompleted += new DownloadStringCompletedEventHandler(flickrCall_Completed);
            flickrCall.DownloadStringAsync(new Uri(url));
        }
        private void flickrCall_Completed(object sender, DownloadStringCompletedEventArgs e) {

            XDocument xmlPhotos = XDocument.Parse(e.Result);
            if (e.Error != null ||
                xmlPhotos.Element("rsp").Attribute("stat").Value == "fail") {
                string results = e.Result;
                return;
            }

            Photos = from photo in xmlPhotos.Element("rsp").Element("photos").Descendants().ToList()
                     select new FlickrPhoto
                     {
                         ID  = (string)photo.Attribute("id"),
                         Owner = (string)photo.Attribute("owner"),
                         Secret = (string)photo.Attribute("secret"),
                         Server = (string)photo.Attribute("server"),
                         Farm = (string)photo.Attribute("farm"),
                         Title = (string)photo.Attribute("title"),
                     };

            // which image should be the initial front item
            // 1 starts at the beginning and
            // (Int32)Math.Ceiling(((Double)Photos.Count() / (Double)2)); would start it in the middle
            CurrentItem = 1;

            Int32 index = 7 - CurrentItem;

            foreach (FlickrPhoto serviceImage in Photos) {
                
                CarouselItem image = new CarouselItem(serviceImage);
                image.Name = "image" + serviceImage.ID;
                image.Width = 480;
                image.Height = 450;
                image.VerticalAlignment = VerticalAlignment.Bottom;
                image.SetValue(Canvas.LeftProperty, LeftMargin(index));
                image.SetValue(Canvas.TopProperty, TopMargin(index));
                image.SetValue(Canvas.ZIndexProperty, ZIndex(index));

                image.MouseLeftButtonUp += new MouseButtonEventHandler(carousel_Click);

                Container.Children.Add(image);

                // hide the images that are far back in the slideshow
                if (index < 1 || index > 11)
                    image.SetValue(Canvas.OpacityProperty, 0.0);
                else
                    image.SetValue(Canvas.OpacityProperty, 1.0);

                // if this is the primary image show the flip icon
                if (index == 6) image.ShowFlipImage();

                Double imageScaleSize = (Double)Size(index);
                image.RenderTransform = GetImageTransformGroup(imageScaleSize);
                image.RenderTransformOrigin = new Point(0, 1);

                image.Projection = new PlaneProjection();

                AddImageStoryboard(serviceImage.ID, image);

                index++;
            }
        }
        private TransformGroup GetImageTransformGroup(Double scaleSize) {
            TransformGroup tg = new TransformGroup();

            TranslateTransform tt = new TranslateTransform();
            tt.X = 0;
            tt.Y = 0;
            tg.Children.Add(tt);

            ScaleTransform st = new ScaleTransform();
            st.ScaleX = scaleSize;
            st.ScaleY = scaleSize;
            tg.Children.Add(st);

            RotateTransform rt = new RotateTransform();
            rt.Angle = 0;
            rt.CenterX = tt.X;
            rt.CenterY = tt.Y;
            tg.Children.Add(rt);

            return tg;
        }
        private void AddImageStoryboard(String imageID, CarouselItem image) {
            Storyboard alterImage = new Storyboard();

            if (!Container.Resources.Contains("image" + imageID + "_move"))
                Container.Resources.Add("image" + imageID + "_move", alterImage);

            // Scale X
            DoubleAnimation scaleTransformX = new DoubleAnimation();
            alterImage.Children.Add(scaleTransformX);
            scaleTransformX.Duration = new Duration(TimeSpan.FromSeconds(1));
            scaleTransformX.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(scaleTransformX, image);
            Storyboard.SetTargetProperty(scaleTransformX,
                                         new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleX)"));
            // Scale Y
            DoubleAnimation scaleTransformY = new DoubleAnimation();
            alterImage.Children.Add(scaleTransformY);
            scaleTransformY.Duration = new Duration(TimeSpan.FromSeconds(1));
            scaleTransformY.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(scaleTransformY, image);
            Storyboard.SetTargetProperty(scaleTransformY,
                                         new PropertyPath("(UIElement.RenderTransform).(TransformGroup.Children)[1].(ScaleTransform.ScaleY)"));

            // Move X
            DoubleAnimation moveX = new DoubleAnimation();
            alterImage.Children.Add(moveX);
            moveX.Duration = new Duration(TimeSpan.FromSeconds(1));
            moveX.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(moveX, image);
            Storyboard.SetTargetProperty(moveX, new PropertyPath("(Canvas.Left)"));

            // Move Y
            DoubleAnimation moveY = new DoubleAnimation();
            alterImage.Children.Add(moveY);
            moveY.Duration = new Duration(TimeSpan.FromSeconds(1));
            moveY.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(moveY, image);
            Storyboard.SetTargetProperty(moveY, new PropertyPath("(Canvas.Top)"));

            // Move Y
            DoubleAnimation opacity = new DoubleAnimation();
            alterImage.Children.Add(opacity);
            opacity.Duration = new Duration(TimeSpan.FromSeconds(1));
            opacity.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(opacity, image);
            Storyboard.SetTargetProperty(opacity, new PropertyPath("(UIElement.Opacity)"));

            // Rotate Y
            DoubleAnimation rotateY = new DoubleAnimation();
            alterImage.Children.Add(rotateY);
            rotateY.Duration = new Duration(TimeSpan.FromSeconds(1));
            rotateY.SetValue(Storyboard.TargetNameProperty, image.Name);
            Storyboard.SetTarget(rotateY, image);
            Storyboard.SetTargetProperty(rotateY,
                                         new PropertyPath("(UIElement.Projection).(PlaneProjection.RotationY)"));

        }

        #region Animation for Moving Images around the Slideshow
        private void carousel_Click(object sender, MouseButtonEventArgs e) {
            if (sender is CarouselItem) {
                CarouselItem clickedItem = (CarouselItem)sender;
                Int32 clickedItemListPosition = (Container.Children.IndexOf(clickedItem) + 1);

                CarouselItem currentItem = (CarouselItem)Container.Children[CurrentItem - 1];

                if (currentItem.Flipped && GetPosition(clickedItem) > 6)
                    currentItem.Flip(false);
                else if (currentItem.Flipped && GetPosition(clickedItem) < 6)
                    currentItem.Flip(true);

                MoveTheCarousel(clickedItemListPosition);
            }
        }
        private void MoveTheCarousel(Int32 clickedItemListPosition) {
            if (AnimationRetainCount == 0) {
                Int32 previousItemListPosition = CurrentItem;
                CurrentItem = clickedItemListPosition;
                Int32 spotsToMove = previousItemListPosition - clickedItemListPosition;
                SpotsMoved = spotsToMove;

                // if the user clicked on the current image then don't move the carousel
                if (spotsToMove == 0) return;

                Int32 itemCarouselPosition = (7 - previousItemListPosition);

                foreach (UIElement uie in Container.Children) {
                    if (uie is CarouselItem) {
                        CarouselItem carouselItem = (CarouselItem)uie;
                        carouselItem.HideFlipImage();
                        Int32 itemListPosition = (Container.Children.IndexOf(carouselItem) + 1);
                        Int32 finalCarouselPosition = GetFinalPosition(carouselItem);

                        AnimationRetainCount++;
                        MoveItemOneStep(carouselItem);
                    }

                    itemCarouselPosition++;
                }
            }
        }
        private void MoveItemOneStep(CarouselItem carouselItem) {
            Int32 position = GetPosition(carouselItem);
            Int32 initialPosition = GetInitialPosition(carouselItem);
            Int32 finalPosition = GetFinalPosition(carouselItem);
            Int32 stepsMoving = finalPosition - initialPosition;
            Int32 nextPosition = position;

            if (position < finalPosition) {
                nextPosition++;
            } else if (position > finalPosition) {
                nextPosition--;
            }

            if (nextPosition != position && position != 6) {
                Storyboard storyboard = (Storyboard)Container.Resources[carouselItem.Name + "_move"];

                Double size = Size(nextPosition);
                Double leftMargin = LeftMargin(nextPosition);
                Double topMargin = TopMargin(nextPosition);
                Double timeSpan = (Double)Math.Abs(1);

                carouselItem.SetValue(Canvas.ZIndexProperty, ZIndex(nextPosition));


                DoubleAnimation da0 = (DoubleAnimation)storyboard.Children[0];
                da0.To = size;
                da0.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da1 = (DoubleAnimation)storyboard.Children[1];
                da1.To = size;
                da1.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da2 = (DoubleAnimation)storyboard.Children[2];
                da2.To = leftMargin;
                da2.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da3 = (DoubleAnimation)storyboard.Children[3];
                da3.To = topMargin;
                da3.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da4 = (DoubleAnimation)storyboard.Children[4];
                da4.To = (nextPosition < 1 || nextPosition > 11) ? 0.0 : 1.0;
                da4.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));

                if (position == initialPosition && nextPosition != finalPosition) {
                    storyboard.Completed += new EventHandler(moveItemOneStep_Completed);
                } else if (nextPosition == finalPosition) {
                    storyboard.Completed -= new EventHandler(moveItemOneStep_Completed);
                    storyboard.Completed += new EventHandler(finalMove_Completed);
                }

                storyboard.Begin();
            } else if (nextPosition != position && position == 6) {
                if (nextPosition > position) {
                    Storyboard storyboard = (Storyboard)Container.Resources[carouselItem.Name + "_move"];

                    Double size = Size(nextPosition);
                    Double leftMargin = LeftMargin(nextPosition);
                    Double topMargin = TopMargin(nextPosition);
                    Double timeSpan = (Double)Math.Abs(0.5);

                    carouselItem.SetValue(Canvas.ZIndexProperty, 8);


                    DoubleAnimation da0 = (DoubleAnimation)storyboard.Children[0];
                    da0.To = 0.8125;
                    da0.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da1 = (DoubleAnimation)storyboard.Children[1];
                    da1.To = 0.8125;
                    da1.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da2 = (DoubleAnimation)storyboard.Children[2];
                    da2.To = 610.0;
                    da2.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da3 = (DoubleAnimation)storyboard.Children[3];
                    da3.To = -20.0;
                    da3.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da4 = (DoubleAnimation)storyboard.Children[4];
                    da4.To = 1.0;
                    da4.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da5 = (DoubleAnimation)storyboard.Children[5];
                    da5.To = -30;
                    da5.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));

                    storyboard.Completed += new EventHandler(movePrimaryFirstPart_Completed);

                    if (position != initialPosition) {
                        storyboard.Completed -= new EventHandler(moveItemOneStep_Completed);
                    }

                    storyboard.Begin();
                } else if (nextPosition < position) {
                    Storyboard storyboard = (Storyboard)Container.Resources[carouselItem.Name + "_move"];

                    Double size = Size(nextPosition);
                    Double leftMargin = LeftMargin(nextPosition);
                    Double topMargin = TopMargin(nextPosition);
                    Double timeSpan = (Double)Math.Abs(0.5);

                    carouselItem.SetValue(Canvas.ZIndexProperty, 8);


                    DoubleAnimation da0 = (DoubleAnimation)storyboard.Children[0];
                    da0.To = 0.8125;
                    da0.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da1 = (DoubleAnimation)storyboard.Children[1];
                    da1.To = 0.8125;
                    da1.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da2 = (DoubleAnimation)storyboard.Children[2];
                    da2.To = -30.0;
                    da2.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da3 = (DoubleAnimation)storyboard.Children[3];
                    da3.To = -20.0;
                    da3.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da4 = (DoubleAnimation)storyboard.Children[4];
                    da4.To = 1.0;
                    da4.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                    DoubleAnimation da5 = (DoubleAnimation)storyboard.Children[5];
                    da5.To = 30;
                    da5.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));

                    storyboard.Completed += new EventHandler(movePrimaryFirstPart_Completed);

                    if (position != initialPosition) {
                        storyboard.Completed -= new EventHandler(moveItemOneStep_Completed);
                    }

                    storyboard.Begin();
                }
            }
        }
        private void finalMove_Completed(object sender, EventArgs e) {
            AnimationRetainCount--;
            if (sender is Storyboard) {
                Storyboard storyboard = (Storyboard)sender;
                storyboard.Completed -= new EventHandler(finalMove_Completed);

                String name = storyboard.Children[0].GetValue(Storyboard.TargetNameProperty).ToString();
                CarouselItem carouselItem = (CarouselItem)Container.FindName(name);
                Int32 finalPosition = GetFinalPosition(carouselItem);
                if (finalPosition == 6) {
                    carouselItem.ShowFlipImage();
                }
            }
        }
        private void moveItemOneStep_Completed(object sender, EventArgs e) {
            if (sender is Storyboard) {
                Storyboard storyboard = (Storyboard)sender;

                String name = storyboard.Children[0].GetValue(Storyboard.TargetNameProperty).ToString();
                CarouselItem carouselItem = (CarouselItem)Container.FindName(name);

                MoveItemOneStep(carouselItem);
            }
        }
        private void movePrimaryFirstPart_Completed(object sender, EventArgs e) {
            if (sender is Storyboard) {
                Storyboard storyboard = (Storyboard)sender;
                storyboard.Stop();
                String name = storyboard.Children[0].GetValue(Storyboard.TargetNameProperty).ToString();

                CarouselItem carouselItem = (CarouselItem)Container.FindName(name);

                Int32 finalPosition = GetFinalPosition(carouselItem);
                Int32 nextPosition = finalPosition > 6 ? 7 : 5;
                Double rotationStartAmount = finalPosition > 6 ? -30.0 : 30.0;

                carouselItem.SetValue(Canvas.ZIndexProperty, ZIndex(finalPosition));

                Double size = Size(nextPosition);
                Double startingLeftMargin = nextPosition == 7 ? 610 : 0;
                Double leftMargin = LeftMargin(nextPosition);
                Double topMargin = TopMargin(nextPosition);
                Double timeSpan = (Double)Math.Abs(0.5);

                carouselItem.SetValue(Canvas.ZIndexProperty, ZIndex(nextPosition));

                DoubleAnimation da0 = (DoubleAnimation)storyboard.Children[0];
                da0.From = 0.8125;
                da0.To = size;
                da0.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da1 = (DoubleAnimation)storyboard.Children[1];
                da1.From = 0.8125;
                da1.To = size;
                da1.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da2 = (DoubleAnimation)storyboard.Children[2];
                da2.From = startingLeftMargin;
                da2.To = leftMargin;
                da2.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da3 = (DoubleAnimation)storyboard.Children[3];
                da3.From = -20.0;
                da3.To = topMargin;
                da3.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da4 = (DoubleAnimation)storyboard.Children[4];
                da4.From = 1.0;
                da4.To = 1.0;
                da4.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));
                DoubleAnimation da5 = (DoubleAnimation)storyboard.Children[5];
                da5.From = rotationStartAmount;
                da5.To = 0;
                da5.Duration = new Duration(TimeSpan.FromSeconds(timeSpan));

                storyboard.Completed -= new EventHandler(movePrimaryFirstPart_Completed);
                storyboard.Completed += new EventHandler(movePrimarySecondPart_Completed);

                storyboard.Begin();
            }
        }
        private void movePrimarySecondPart_Completed(object sender, EventArgs e) {
            if (sender is Storyboard) {
                Storyboard storyboard = (Storyboard)sender;

                DoubleAnimation da0 = (DoubleAnimation)storyboard.Children[0];
                da0.From = null;
                DoubleAnimation da1 = (DoubleAnimation)storyboard.Children[1];
                da1.From = null;
                DoubleAnimation da2 = (DoubleAnimation)storyboard.Children[2];
                da2.From = null;
                DoubleAnimation da3 = (DoubleAnimation)storyboard.Children[3];
                da3.From = null;
                DoubleAnimation da4 = (DoubleAnimation)storyboard.Children[4];
                da4.From = null;
                DoubleAnimation da5 = (DoubleAnimation)storyboard.Children[5];
                da5.From = null;

                storyboard.Completed -= new EventHandler(movePrimarySecondPart_Completed);
                String name = storyboard.Children[0].GetValue(Storyboard.TargetNameProperty).ToString();
                CarouselItem carouselItem = (CarouselItem)Container.FindName(name);
                
                Int32 position = GetPosition(carouselItem);
                Int32 finalPosition = GetFinalPosition(carouselItem);

                if (position != finalPosition) {
                    storyboard.Completed += new EventHandler(moveItemOneStep_Completed);
                    MoveItemOneStep(carouselItem);
                } else {
                    AnimationRetainCount--;
                }
            }
        }
        #endregion Animation for Moving Images around the Slideshow

        #region Get Measurements Based On Image Position
        private Double Size(Int32 position) {
            Double size = 0.325;

            if (position == 6)
                size = 1;
            else if (position == 5 || position == 7)
                size = 0.625;
            else if (position == 4 || position == 8)
                size = 0.55;
            else if (position == 3 || position == 9)
                size = 0.475;
            else if (position == 2 || position == 10)
                size = 0.4;
            else if (position == 1 || position == 11)
                size = 0.325;

            return size;
        }
        private Double TopMargin(Int32 position) {
            Double margin = 0;

            if (position == 6)
                margin = 0;
            else if (position == 5 || position == 7)
                margin = -40;
            else if (position == 4 || position == 8)
                margin = -56;
            else if (position == 3 || position == 9)
                margin = -75;
            else if (position == 2 || position == 10)
                margin = -90;
            else if (position < 2 || position > 10)
                margin = -105;

            return margin;
        }
        private Int32 ZIndex(Int32 position) {
            Int32 zindex = 1;

            if (position == 6)
                zindex = 7;
            else if (position == 5 || position == 7)
                zindex = 6;
            else if (position == 4 || position == 8)
                zindex = 5;
            else if (position == 3 || position == 9)
                zindex = 4;
            else if (position == 2 || position == 10)
                zindex = 3;
            else if (position == 1 || position == 11)
                zindex = 2;

            return zindex;
        }
        private Double LeftMargin(Int32 position) {
            Double margin = 1;

            if (position == 6)
                margin = 240;
            else if (position == 5)
                margin = 150;
            else if (position == 4)
                margin = 102;
            else if (position == 3)
                margin = 76;
            else if (position == 2)
                margin = 42;
            else if (position < 2)
                margin = 10;
            else if (position == 7)
                margin = 510;
            else if (position == 8)
                margin = 594;
            else if (position == 9)
                margin = 656;
            else if (position == 10)
                margin = 726;
            else if (position > 10)
                margin = 794;

            return margin;
        }
        #endregion Get Measurements Based On Image Position

        #region Get a Specific Image's Positions in the Slideshow
        private Int32 GetFinalPosition(CarouselItem item) {
            Int32 itemIndex = (Container.Children.IndexOf(item) + 1);
            Int32 selectedIndex = (7 - CurrentItem);
            Int32 finalPosition = 6 - (CurrentItem - itemIndex);
            return finalPosition;
        }
        private Int32 GetInitialPosition(CarouselItem item) {
            Int32 itemPositionInList = (Container.Children.IndexOf(item) + 1);
            Int32 itemDistanceFromCurrentItem = CurrentItem - itemPositionInList;
            CarouselItem currentItem = (CarouselItem)Container.Children[CurrentItem - 1];
            Int32 finalCarouselPositionOfCurrentItem = GetFinalPosition(currentItem);
            Int32 initialCarouselPositionOfCurrentItem = finalCarouselPositionOfCurrentItem - SpotsMoved;
            Int32 initialCarouselPositionOfItem = initialCarouselPositionOfCurrentItem - itemDistanceFromCurrentItem;
            return initialCarouselPositionOfItem;
        }
        private Int32 GetPosition(CarouselItem carouselItem) {
            
            Double leftMargin = (Double)carouselItem.GetValue(Canvas.LeftProperty);
            Int32 position = CheckPositionFromLeftMargin(leftMargin);

            if (position == 0) {

                Int32 indexOfThisItem = Container.Children.IndexOf(carouselItem);

                foreach (UIElement uie in Container.Children) {
                    if (uie is CarouselItem) {
                        CarouselItem loopItem = (CarouselItem)uie;
                        Double loopItemMargin = (Double)loopItem.GetValue(Canvas.LeftProperty);
                        Int32 loopItemPosition = CheckPositionFromLeftMargin(loopItemMargin);
                        if (loopItemPosition != 0) {
                            Int32 loopItemIndex = Container.Children.IndexOf(loopItem);
                            Int32 indexDifference = loopItemIndex - indexOfThisItem;
                            position = loopItemPosition - indexDifference;
                        }
                    }
                    if (position != 0) break;
                }
            }
            return position;
        }
        private Int32 CheckPositionFromLeftMargin(Double leftMargin) {
            Int32 position = 0;
            for (Int32 i = 2; i < 11; i++) {
                Double margin = LeftMargin(i);
                if (margin == leftMargin)
                    position = i;
            }
            return position;
        }
        #endregion Get a Specific Image's Positions in the Slideshow
    }
}
